package com.stars.easyms.logger.support.logback.appender;

import ch.qos.logback.classic.ClassicConstants;
import ch.qos.logback.classic.sift.MDCBasedDiscriminator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.OutputStreamAppender;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.joran.spi.DefaultClass;
import ch.qos.logback.core.sift.AppenderFactory;
import ch.qos.logback.core.sift.Discriminator;
import ch.qos.logback.core.util.Duration;
import com.stars.easyms.logger.config.EasyMsLoggerHelper;
import com.stars.easyms.logger.support.EasyMsOverride;
import com.stars.easyms.logger.support.logback.handler.EasyMsAsynAppenderAddExecutor;
import com.stars.easyms.logger.support.logback.handler.EasyMsAsynAppenderEncoderCache;
import com.stars.easyms.logger.support.logback.handler.EasyMsLoggingEvent;
import com.stars.easyms.logger.support.logback.sift.EasyMsAppenderTracker;
import com.stars.easyms.logger.support.logback.util.EasyMsAsynAppenderAddExecutorHolder;
import org.slf4j.Marker;

import java.util.List;

/**
 * <p>className: EasyMsUnsynchronizedSiftingAppender</p>
 * <p>description: 重写SiftingAppender类(非线程安全)</p>
 *
 * @author guoguifang
 * @date 2019-08-20 15:46
 * @since 1.3.0
 */
public final class EasyMsUnsynchronizedSiftingAppender extends UnsynchronizedAppenderBase<ILoggingEvent> implements EasyMsOverride {

    private EasyMsAppenderTracker<ILoggingEvent> appenderTracker;

    private AppenderFactory<ILoggingEvent> appenderFactory;

    private Duration timeout = new Duration(1800000);

    private int maxAppenderCount = Integer.MAX_VALUE;

    private Discriminator<ILoggingEvent> discriminator;

    @Override
    public void start() {
        int errors = 0;
        if (discriminator == null) {
            addError("Missing discriminator. Aborting");
            errors++;
        }
        if (!discriminator.isStarted()) {
            addError("Discriminator has not started successfully. Aborting");
            errors++;
        }
        if (appenderFactory == null) {
            addError("AppenderFactory has not been set. Aborting");
            errors++;
        } else {
            appenderTracker = new EasyMsAppenderTracker<>(context, appenderFactory);
            appenderTracker.setMaxComponents(maxAppenderCount);
            appenderTracker.setTimeout(timeout.getMilliseconds());
        }
        if (errors == 0) {
            super.start();
        }
    }

    @Override
    public void stop() {
        for (Appender<ILoggingEvent> appender : appenderTracker.allComponents()) {
            appender.stop();
        }
    }

    @Override
    public void doAppend(ILoggingEvent event) {
        if (EasyMsLoggerHelper.isSiftAsynEnabled()) {
            super.doAppend(event);
        } else {
            synchronized (this) {
                super.doAppend(event);
            }
        }
    }

    @Override
    protected void append(ILoggingEvent event) {
        if (!isStarted()) {
            return;
        }
        String discriminatingValue = discriminator.getDiscriminatingValue(event);
        long timestamp = getTimestamp(event);

        Appender<ILoggingEvent> appender = appenderTracker.getOrCreate(discriminatingValue, timestamp);
        if (eventMarksEndOfLife(event)) {
            appenderTracker.endOfLife(discriminatingValue);
        }
        appenderTracker.removeStaleComponents(timestamp);
        if (appender instanceof OutputStreamAppender && event instanceof EasyMsLoggingEvent) {
            List<EasyMsAsynAppenderAddExecutor> easyMsAsynAppenderAddExecutorList = EasyMsAsynAppenderAddExecutorHolder.getAsynAppenderAddExecutorList();
            if (easyMsAsynAppenderAddExecutorList != null) {
                easyMsAsynAppenderAddExecutorList.add(EasyMsAsynAppenderEncoderCache.getAddExecutor((OutputStreamAppender) appender, event));
            } else {
                EasyMsAsynAppenderEncoderCache.execute((OutputStreamAppender) appender, (EasyMsLoggingEvent) event);
            }
        } else {
            appender.doAppend(event);
        }
    }

    public Duration getTimeout() {
        return timeout;
    }

    public void setTimeout(Duration timeout) {
        this.timeout = timeout;
    }

    public int getMaxAppenderCount() {
        return maxAppenderCount;
    }

    public void setMaxAppenderCount(int maxAppenderCount) {
        this.maxAppenderCount = maxAppenderCount;
    }

    public void setAppenderFactory(AppenderFactory<ILoggingEvent> appenderFactory) {
        this.appenderFactory = appenderFactory;
    }

    @DefaultClass(MDCBasedDiscriminator.class)
    public void setDiscriminator(Discriminator<ILoggingEvent> discriminator) {
        this.discriminator = discriminator;
    }

    public Discriminator<ILoggingEvent> getDiscriminator() {
        return discriminator;
    }

    public EasyMsAppenderTracker<ILoggingEvent> getAppenderTracker() {
        return appenderTracker;
    }

    public String getDiscriminatorKey() {
        if (discriminator != null) {
            return discriminator.getKey();
        } else {
            return null;
        }
    }

    private long getTimestamp(ILoggingEvent event) {
        return event.getTimeStamp();
    }

    private boolean eventMarksEndOfLife(ILoggingEvent event) {
        Marker marker = event.getMarker();
        if (marker == null) {
            return false;
        }
        return marker.contains(ClassicConstants.FINALIZE_SESSION_MARKER);
    }

}